// Copyright 2010-2012, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_LOCK_H_
#define LIBV_LOCK_H_

#include <libv/uncopyable.h>

/** @file Внимание! Если при использовании данных блокировок под ОС Windows
  * компилятор выдает ошибку при использовании метода TryEnterCriticalSection,
  * значит необходимо перенести все заголовочные файлы, связанные с блокировками
  * lock*.h до остальных. Ошибка связана с тем, что по умолчанию в windows.h
  * отключена поддержка данной функции и ее нужно явно активировать макросом.
  * Однако, если они из других заголовочных файлов уже включил windows.h, то
  * за счет header guards libv не сможет подключить его, что и приводит к
  * ошибке. См. http://msdn.microsoft.com/en-us/library/ms686857(VS.85).aspx */

namespace libv
{

// Использование интерфейсов вместо шаблонов позволяет не задавать тип
// блокировки при каждом использовании класса Lock, так как аргументы шаблона
// не могут быть выведены в конструкторе.
// Таким образом взятие блокировки выглядит как
// Lock dnd(&lock_), а не
// Lock<Mutex> dnd(&lock_), что уменьшает дублирование кода.
// В качестве платы: увеличение каждого класса блокировки на 1 размер указателя
// (указатель на таблицу vptr) и небольшое замедление вызовов методов.
// Для перехода от "интерфейсной" реализации к "шаблонной" необходимо сделать
// всего 2 действия:
//   - изменить конструктор Lock на шаблонный
//   - изменить все интерфейсы на пустые классы.
// Так как класс блокировок удовлетворяют интерфейсам, проверяемые компилятором,
// то они так же удовлетворяют непроверяемым концепциям.
// При наследовании класса, содержащего виртуальные методы, все перекрывающие
// методы в производных классах также неявно становятся виртуальными. Поэтому
// ключевое слово 'virtual' — подсказка человеку. Но, если подменить интерфейс
// пустым базовым классом, то неперекрывающие, не объявленные виртуальными
// методы наследников станут обычными методами. Более того, оптимизатор выполнит
// "оптимизацию пустого базового класса", исключив его из иерархии.


class ResourceInterface : Uncopyable
{
  public:
    virtual ~ResourceInterface() {}
};


class LockableResourceInterface : public ResourceInterface
{
  public:
    virtual void Lock() = 0;
    virtual bool TryLock() = 0;
    virtual void Unlock() = 0;
};


class RecursiveLockableResourceInterface : public LockableResourceInterface
{
    // Пока здесь ничего нет.
};


class ReadWriteLockableResourceInterface : public ResourceInterface
{
  public:
    // Блокировка должна хранить свой тип и вызывать соответствующий Unlock.
    // В таком случае число полей в мьютексах станет меньше -> экономия памяти.

    virtual bool TryReadLock() = 0;
    virtual void ReadLock() = 0;
    virtual void ReadUnlock() = 0;

    virtual bool TryWriteLock() = 0;
    virtual void WriteLock() = 0;
    virtual void WriteUnlock() = 0;
};


////////////////////////////////////////////////////////////////////////////////


class Lock : Uncopyable
{
  public:
    explicit Lock(LockableResourceInterface* resource)
        : resource_(resource),
          is_locked_(true)
    {
        resource->Lock();
    }

    void Unlock()
    {
        if ( is_locked_ )
        {
            resource_->Unlock();
            is_locked_ = false;  // на всякий is_locked_ отключаем первым.
            resource_ = static_cast<LockableResourceInterface*>(NULL);
        }
    }

    ~Lock()
    {
        Unlock();
    }


  private:
    LockableResourceInterface* resource_;
    bool is_locked_;
};


////////////////////////////////////////////////////////////////////////////////


class WriteLock : Uncopyable
{
  public:
    explicit WriteLock(ReadWriteLockableResourceInterface* resource)
        : resource_(resource),
          is_locked_(true)
    {
        resource->WriteLock();
    }

    void Unlock()
    {
        if ( is_locked_ )
        {
            resource_->WriteUnlock();
            is_locked_ = false;
            resource_ = static_cast<ReadWriteLockableResourceInterface*>(NULL);
        }
    }

    ~WriteLock()
    {
        Unlock();
    }


  private:
    ReadWriteLockableResourceInterface* resource_;
    bool is_locked_;
};



class ReadLock : Uncopyable
{
  public:
    explicit ReadLock(ReadWriteLockableResourceInterface* resource)
        : resource_(resource),
          is_locked_(true)
    {
        resource->ReadLock();
    }

    void Unlock()
    {
        if ( is_locked_ )
        {
            resource_->ReadUnlock();
            is_locked_ = false;
            resource_ = static_cast<ReadWriteLockableResourceInterface*>(NULL);
        }
    }

    ~ReadLock()
    {
        Unlock();
    }


  private:
    ReadWriteLockableResourceInterface* resource_;
    bool is_locked_;
};


}  // libv

#endif  // LIBV_LOCK_H_
